﻿// -----------------------------------------------------------------------
// <copyright file="PlaylistController.cs" company="Abyssalsoft">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace Metal.Player.Playlist
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Drawing;
    using System.IO;
    using System.Reflection;
    using System.Text;
    using System.Windows.Forms;
    using System.Xml;
    using Metal.Config;

    /// <summary>
    /// Playlist controller
    /// </summary>
    public class PlaylistController
    {
        #region Members

        /// <summary>Stores our view to communicate with the mask</summary>
        private static IPlaylistView view;

        /// <summary>The data set used to bind playlist</summary>
        private static DataSet listDataSet;

        /// <summary>The data set used to bind playlist</summary>
        private static DataSet playlistsDataSet;

        /// <summary>Stores the current playlist name</summary>
        private static string currentPlaylistName;

        private static string currentFileName;

        /// <summary>Stores the current file index</summary>
        private static int currentFileIndex;

        private string previousFileName;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the PlaylistController class.
        /// </summary>
        /// <param name="iView">Interface to communicate with the mask</param>
        public PlaylistController(IPlaylistView iView)
        {
            view = iView;
            this.SubscribeToViewEvents();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the playlist data set
        /// </summary>
        public static DataSet ListDataSet
        {
            get
            {
                return listDataSet;
            }

            set
            {
                listDataSet = value;
            }
        }

        /// <summary>
        /// Gets or sets the playlists data set
        /// </summary>
        public static DataSet PlaylistsDataSet
        {
            get
            {
                return playlistsDataSet;
            }

            set
            {
                playlistsDataSet = value;
            }
        }

        /// <summary>
        /// Gets or sets the interface to communicate with the mask
        /// </summary>
        internal static IPlaylistView CurrentView
        {
            get
            {
                return view;
            }

            set
            {
                view = value;
            }
        }

        /// <summary>
        /// Gets or sets the current playlist name
        /// </summary>
        internal static string CurrentListName
        {
            get
            {
                return currentPlaylistName;
            }

            set
            {
                currentPlaylistName = value;
            }
        }

        internal static string CurrentFileName
        {
            get
            {
                return currentFileName;
            }

            set
            {
                currentFileName = value;
            }
        }

        internal static int CurrentFileIndex
        {
            get
            {
                return currentFileIndex;
            }

            set
            {
                currentFileIndex = value;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Save the current playlist
        /// </summary>
        public static void SaveCurrentPlaylist()
        {
            try
            {
                XmlWriterSettings xmlSettings = new XmlWriterSettings();
                xmlSettings.Indent = true;
                xmlSettings.NewLineOnAttributes = true;

                XmlWriter xmlPlaylist;
                xmlPlaylist = XmlWriter.Create(Metal.Config.Paths.PlaylistsFolder + CurrentListName + ".xml", xmlSettings);
                ListDataSet.WriteXml(xmlPlaylist);
                xmlPlaylist.Close();
            }
            catch (Exception ex)
            {
                Metal.User.UserMessage.ShowError(ex);
            }
        }

        /// <summary>
        /// Select specific playlists row
        /// </summary>
        /// <param name="columnIndex">The column that contains our search key</param>
        /// <param name="value">Search for this row value</param>
        public static void SelectPlaylistsRow(int columnIndex, string value)
        {
            // Select our new list
            int index = 0;
            for (int i = 0; i < PlaylistsDataSet.Tables[0].Rows.Count; i++)
            {
                if (PlaylistsDataSet.Tables[0].Rows[i][columnIndex].ToString() == value)
                {
                    index = i;
                }
            }

            CurrentView.SelectPlaylistsRow = index;
        }

        /// <summary>
        /// Select specific playlists row
        /// </summary>
        /// <param name="columnIndex">The column that contains our search key</param>
        /// <param name="value">Search for this row value</param>
        public static void SelectListRow(int columnIndex, string value)
        {
            // Select our new list
            int index = 0;
            for (int i = 0; i < ListDataSet.Tables[0].Rows.Count; i++)
            {
                if (ListDataSet.Tables[0].Rows[i][columnIndex].ToString() == value)
                {
                    index = i;
                }
            }
            
            CurrentView.SelectListRow = index;
        }        

        /// <summary>
        /// Sets the current view (audio / video form)
        /// </summary>
        /// <param name="iView">The view which we must control</param>
        public void SetCurrentView(IPlaylistView iView)
        {
            CurrentView = iView;

            if (previousFileName != CurrentFileName)
            {
                SelectListRow(2, CurrentFileName);
                SelectPlaylistsRow(2, PlaylistController.CurrentListName);
            }

            previousFileName = CurrentFileName;
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Subscribe to events defined in the view
        /// </summary>
        private void SubscribeToViewEvents()
        {
            view.MaskLoad += new EventHandler(this.OnMaskLoad);
            view.MaskClose += new EventHandler(this.OnMaskClose);
            view.ControlAdded += new System.Windows.Forms.ControlEventHandler(this.OnControlAdded);
            view.ListDragDrop += new DragEventHandler(this.OnListDragDrop);

            // Player events
            Core.Player.Events.Instance.PlaybackEnded += new EventHandler(this.OnPlaybackEnded);
        }

        /// <summary>
        /// Do something when the user select a item in playlist
        /// </summary>
        private void ListClick()
        {
            if (PlaylistController.CurrentView.ListSelectedRow == null)
            {
                return;
            }

            SelectPlaylistsRow(2, PlaylistController.CurrentListName);
        }

        /// <summary>
        /// Open selected playlist
        /// </summary>
        private void PlaylistsClick()
        {
            try
            {
                if (PlaylistController.CurrentView.PlaylistsSelectedRow != null)
                {
                    // Load selected list in the playlist
                    ListDataSet.Clear();
                    ListDataSet.ReadXml(PlaylistController.CurrentView.PlaylistsSelectedRow.Cells[1].Value.ToString());
                    CurrentListName = PlaylistController.CurrentView.PlaylistsSelectedRow.Cells[2].Value.ToString();

                    // Update
                    view.ListDataSet = ListDataSet;

                    // Since the list is changed, reset last played index
                    CurrentFileIndex = 0;
                }
            }
            catch (Exception ex)
            {
                Metal.User.UserMessage.ShowError(ex);
            }
        }

        /// <summary>
        /// Play selected file
        /// </summary>
        private void ListDoubleClick()
        {
            try
            {
                if (PlaylistController.CurrentView.ListSelectedRow != null)
                {
                    CurrentFileName =  CurrentView.ListSelectedRow.Cells[2].Value.ToString();
                    CurrentFileIndex = Convert.ToInt32(PlaylistController.CurrentView.ListSelectedRow.Index);

                    string filePath = PlaylistController.CurrentView.ListSelectedRow.Cells[1].Value.ToString();
                    Core.Player.Playback.Instance.Open(filePath);
                    Core.Player.Playback.Instance.Play();
                }
            }
            catch (Exception ex)
            {
                Metal.User.UserMessage.ShowError(ex);
            }
        }

        /// <summary>
        /// Load last playlist
        /// </summary>
        private void LoadLastList()
        {
            try
            {
                CurrentListName = Settings.Instance.GetValue("LastSettings.LastPlaylist", "Default list");

                // Load list data set
                ListDataSet = new DataSet();

                // Get the dataset with all neccessary columns (loaded from theme xml)
                ListDataSet = CurrentView.ListDataSet;

                // Load last playlist in our dataset
                ListDataSet.ReadXml(Metal.Config.Paths.PlaylistsFolder + Settings.Instance.GetValue("LastSettings.LastPlaylist", "Default list") + ".xml");

                // Update
                CurrentView.ListDataSet = ListDataSet;
            }
            catch
            {
                // We can ignore it I think...
            }
        }

        /// <summary>
        /// Load the available lists
        /// </summary>
        private void LoadListOfPlaylists()
        {
            try
            {
                // Load the list of playlists data set
                PlaylistsDataSet = new DataSet();

                // Get the dataset with all neccessary columns (loaded from theme xml)
                PlaylistsDataSet = view.PlaylistsDataSet;

                string[] files = System.IO.Directory.GetFiles(Metal.Config.Paths.PlaylistsFolder);

                int nrOfLists = 0;

                foreach (string file in files)
                {
                    playlistsDataSet.Tables[0].Rows.Add(nrOfLists, file, System.IO.Path.GetFileNameWithoutExtension(file));
                    nrOfLists++;
                }

                // Update
                CurrentView.PlaylistsDataSet = PlaylistsDataSet;

                SelectPlaylistsRow(2, PlaylistController.CurrentListName);
            }
            catch
            {
            }
        }

        #endregion

        #region Mask events

        /// <summary>
        /// This method is called when the mask is loaded.
        /// </summary>
        /// <param name="sender">Object sender</param>
        /// <param name="e">Event arguments</param>
        private void OnMaskLoad(object sender, EventArgs e)
        {
            try
            {
                // Load last playlist
                this.LoadLastList();

                // Load the list of playlists
                this.LoadListOfPlaylists();

                // Show / hide list of playlists
                if (Settings.Instance.GetValue("LastSettings.ShowListOfPlaylists", false) == true)
                {
                    CurrentView.PlaylistsVisible = true;
                }
                else
                {
                    CurrentView.PlaylistsVisible = false;
                }
            }
            catch (Exception ex)
            {
                Metal.User.UserMessage.ShowError(ex);
            }
        }

        /// <summary>
        /// This method is called when the mask is closed.
        /// </summary>
        /// <param name="sender">Object sender</param>
        /// <param name="e">Event arguments</param>
        private void OnMaskClose(object sender, EventArgs e)
        {
            try
            {
                // Save playlist name
                Settings.Instance.SetValue("LastSettings.LastPlaylist", CurrentListName);

                // Write some close logic here.
                SaveCurrentPlaylist();
            }
            catch (Exception ex)
            {
                Metal.User.UserMessage.ShowError(ex);
            }
        }

        /// <summary>
        /// This method is called when a control is added on the mask
        /// </summary>
        /// <param name="sender">Object sender</param>
        /// <param name="e">Event arguments</param>
        private void OnControlAdded(object sender, System.Windows.Forms.ControlEventArgs e)
        {
            try
            {
                if (e.Control.GetType() == typeof(Gui.Controls.MetalList))
                {
                    e.Control.Click += new EventHandler(this.OnListClick);
                    e.Control.DoubleClick += new EventHandler(this.OnListDoubleClick);
                    e.Control.MouseDown += new MouseEventHandler(this.OnListMouseDown);
                    if ((e.Control as Gui.Controls.MetalList).ContextMenuStrip != null)
                    {
                        (e.Control as Gui.Controls.MetalList).ContextMenuStrip.ItemClicked += new ToolStripItemClickedEventHandler(this.OnContextMenuClick);
                    }
                }
                else if (e.Control.GetType() == typeof(Gui.Controls.MetalPanel))
                {
                    e.Control.ControlAdded += new System.Windows.Forms.ControlEventHandler(this.OnControlAdded);
                }
            }
            catch (Exception ex)
            {
                Metal.User.UserMessage.ShowError(ex);
            }
        }

        #endregion

        #region Control events

        /// <summary>
        /// This event occurs when the user clicks on menu
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">ToolStripItem event args</param>
        private void OnContextMenuClick(object sender, ToolStripItemClickedEventArgs e)
        {
            try
            {
                string methodName = e.ClickedItem.Tag.ToString();

                // Call the method via reflection
                Type thisType = typeof(PlaylistLogic);
                System.Reflection.MethodInfo theMethod = thisType.GetMethod(methodName, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
                if (theMethod != null)
                {
                    theMethod.Invoke(PlaylistLogic.Instance, null);
                }
            }
            catch (Exception ex)
            {
                Metal.User.UserMessage.ShowError(ex);
            }
        }

        /// <summary>
        /// This event occurs on click event
        /// </summary>
        /// <param name="sender">Object sender</param>
        /// <param name="e">Event args</param>
        private void OnListClick(object sender, EventArgs e)
        {
            try
            {
                Type thisType = typeof(PlaylistController);
                MethodInfo theMethod = thisType.GetMethod(((Gui.Controls.MetalList)sender).Name + "Click", BindingFlags.NonPublic | BindingFlags.Instance);
                if (theMethod != null)
                {
                    theMethod.Invoke(this, null);
                }
            }
            catch (Exception ex)
            {
                Metal.User.UserMessage.ShowError(ex);
            }
        }

        /// <summary>
        /// This event occurs on DoubleClick event
        /// </summary>
        /// <param name="sender">Object sender</param>
        /// <param name="e">Event args</param>
        private void OnListDoubleClick(object sender, EventArgs e)
        {
            try
            {
                Type thisType = typeof(PlaylistController);
                MethodInfo theMethod = thisType.GetMethod(((Gui.Controls.MetalList)sender).Name + "DoubleClick", BindingFlags.NonPublic | BindingFlags.Instance);
                if (theMethod != null)
                {
                    theMethod.Invoke(this, null);
                }
            }
            catch (Exception ex)
            {
                Metal.User.UserMessage.ShowError(ex);
            }
        }

        /// <summary>
        /// This event occurs on mouse down
        /// </summary>
        /// <param name="sender">Object sender</param>
        /// <param name="e">Event args</param>
        private void OnListMouseDown(object sender, MouseEventArgs e)
        {
            try
            {
            }
            catch (Exception ex)
            {
                Metal.User.UserMessage.ShowError(ex);
            }
        }

        /// <summary>
        /// This event occurs on drag and drop
        /// </summary>
        /// <param name="sender">Object sender</param>
        /// <param name="e">Event args</param>
        private void OnListDragDrop(object sender, DragEventArgs e)
        {
            try
            {
                string[] handles = (string[])e.Data.GetData(DataFormats.FileDrop, false);
                if (handles != null)
                {
                    foreach (string s in handles)
                    {
                        if (System.IO.File.Exists(s))
                        {
                            // Is file
                            PlaylistLogic.Instance.AddFileToList(s);
                        }
                        else if (System.IO.Directory.Exists(s))
                        {
                            // Is folder...
                            PlaylistLogic.Instance.AddFolderToList(s);
                        }

                        // Update
                        PlaylistController.CurrentView.ListDataSet = PlaylistController.ListDataSet;
                        if (PlaylistController.CurrentView.ListDataSet.Tables[0].Rows.Count > 0)
                        {
                            SelectListRow(1, s);
                        }
                    }

                    PlaylistController.SaveCurrentPlaylist();
                }
            }
            catch (Exception ex)
            {
                Metal.User.UserMessage.ShowError(ex);
            }
        }

        #endregion

        #region Player events

        /// <summary>
        /// When the playback is ended, do something
        /// </summary>
        /// <param name="sender">Object sender</param>
        /// <param name="e">Event args</param>
        private void OnPlaybackEnded(object sender, EventArgs e)
        {
            try
            {
                if (Core.Player.Status.PlaybackStatus == Core.Player.Status.StatusEnum.Playing)
                {
                    return;
                }

                // If we have something in the queue, play those files
                if (QueueController.ListDataSet.Tables[0].Rows.Count > 0)
                {
                    QueueController.PlayNext();
                    return;
                }

                if (Settings.Instance.GetValue("LastSettings.PlayRecursively", true) == true)
                {
                    CurrentFileIndex++;

                    if (CurrentFileIndex < CurrentView.ListDataSet.Tables[0].Rows.Count)
                    {
                        CurrentView.SelectListRow = CurrentFileIndex;
                        Core.Player.Playback.Instance.Open(PlaylistController.CurrentView.ListSelectedRow.Cells[1].Value.ToString());
                        CurrentFileName = PlaylistController.CurrentView.ListSelectedRow.Cells[2].Value.ToString();
                        Core.Player.Playback.Instance.Play();
                    }
                    else if (CurrentFileIndex == CurrentView.ListDataSet.Tables[0].Rows.Count)
                    {
                        // This this was the last file in current playlist, so jump to next playlist
                        if (Settings.Instance.GetValue("LastSettings.PlayAllLists", true) == true)
                        {
                            int itemIndex = Convert.ToInt32(CurrentView.PlaylistsSelectedRow.Index);

                            // If is not the last playlist in playlists
                            if (itemIndex < CurrentView.PlaylistsDataSet.Tables[0].Rows.Count)
                            {
                                SelectPlaylistsRow(0, itemIndex.ToString());
                                this.PlaylistsClick();
                                Core.Player.Playback.Instance.Open(PlaylistController.CurrentView.ListSelectedRow.Cells[1].Value.ToString());
                                CurrentFileName = PlaylistController.CurrentView.ListSelectedRow.Cells[2].Value.ToString();
                                Core.Player.Playback.Instance.Play();
                            }
                        }
                    }
                }
                else if (Settings.Instance.GetValue("LastSettings.PlayRandom", false) == true)
                {
                    Random rnd = new Random();
                    int randomNumber = rnd.Next(0, CurrentView.ListDataSet.Tables[0].Rows.Count);
                    CurrentView.SelectListRow = randomNumber;
                    Core.Player.Playback.Instance.Open(PlaylistController.CurrentView.ListSelectedRow.Cells[1].Value.ToString());
                    CurrentFileName = PlaylistController.CurrentView.ListSelectedRow.Cells[2].Value.ToString();
                    Core.Player.Playback.Instance.Play();
                }
            }
            catch (Exception ex)
            {
                Metal.User.UserMessage.ShowError(ex);
            }
        }

        #endregion
    }
}
